package springmvc;

import server.request.HttpRequest;
import server.response.HttpResponse;
import server.servlet.HttpServlet;
import spring.ApplicationContext;
import spring.RequestContext;
import spring.annotation.bean.Controller;
import springmvc.annotation.GetMapping;
import springmvc.annotation.PostMapping;
import springmvc.annotation.RequestMapping;
import springmvc.annotation.ResponseBody;
import springmvc.components.adaptor.HandlerAdaptor;
import springmvc.components.mapping.HandlerMapping;
import springmvc.components.view.ModelAndView;
import springmvc.components.view.View;
import springmvc.components.view.ViewResolver;
import springmvc.config.WebConfig;
import springmvc.utils.HandlerUtils;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;

/**
 * @author cg
 * @date 2023/6/16 15:14
 */
public class DispatchServlet extends HttpServlet {
    /**
     * 存放spring容器
     */
    private ApplicationContext context;
    /**
     * 存放请求映射处理器
     */
    private List<HandlerMapping> handlerMappings = new CopyOnWriteArrayList<>();
    /**
     * 存放处理器适配器
     */
    private Map<HandlerMapping, HandlerAdaptor> handlerAdaptorMap = new ConcurrentHashMap<>();
    /**
     * 存放视图解析器
     */
    private List<ViewResolver> viewResolvers = new CopyOnWriteArrayList<>();
    //配置类
    public WebConfig webConfig;

    @Override
    protected void doGet(HttpRequest request, HttpResponse response) {
        doPost(request, response);
    }

    @Override
    protected void doPost(HttpRequest request, HttpResponse response) {
        try {
            //放入请求上下文
            RequestContext.setHttpRequest(request);
            doDispatch(request, response);
        } catch (Exception e) {
            e.printStackTrace();
            response.write500();
        }finally {
            RequestContext.removeHttpRequest();
        }
    }

    /**
     * 处理请求
     *
     * @param request
     * @param response
     */
    private void doDispatch(HttpRequest request, HttpResponse response) throws InvocationTargetException, IllegalAccessException, IOException {
        String method = request.getMethod();
        String url = request.getUrl();
        HandlerMapping handlerMapping = null;
        //找到匹配的映射器
        for (HandlerMapping mapping : handlerMappings) {
            if (mapping.support(url, method)) {
                handlerMapping = mapping;
                break;
            }
        }
        //未找到处理器则抛出404
        if (handlerMapping == null) {
            //查看是否有静态资源
            String realPath = this.getClass().getResource("/").getPath() + url;
            File file = new File(realPath.substring(1));
            if (file.exists()) {
                response.writeResource(url);
                return;
            }
            //todo 优先返回自定义的404界面
            response.write404();
            return;
        }
        //找到映射处理器
        HandlerAdaptor handlerAdaptor = handlerAdaptorMap.get(handlerMapping);
        //执行处理逻辑并返回ModelAndView
        Object result = handlerAdaptor.handle(request, response, handlerMapping);
        //todo 可以使用适配器模式优化代码
        if (handlerMapping.isRender()) {
            //如果是视图类，就进行视图解析
            viewResolve(request, response, result);
        } else {
            //否则则进行结果解析
            StringResolve(request, response, result);
        }
    }

    /**
     * 渲染视图
     *
     * @param request
     * @param response
     * @param result   视图渲染对象
     */
    private void viewResolve(HttpRequest request, HttpResponse response, Object result) throws IOException {
        ModelAndView mv = null;
        if (result instanceof ModelAndView) {
            mv = (ModelAndView) result;
        } else if (result instanceof String) {
            mv = new ModelAndView(new HashMap<>(), (String) result);
        } else {
            throw new RuntimeException("视图解析失败。。");
        }
        String viewName = mv.getViewName();
        Map<String, Object> model = mv.getModel();
        //找到对应的视图解析器
        for (ViewResolver viewResolver : viewResolvers) {
            View view = viewResolver.resolveViewName(viewName);
            if (view == null) continue;
            //解析视图
            view.render(request, response, mv.getModel());
            return;
        }
        throw new RuntimeException("未找到该视图");
    }

    /**
     * 此处只使用了字符串返回
     *
     * @param request
     * @param response
     * @param result
     */
    private void StringResolve(HttpRequest request, HttpResponse response, Object result) {
        response.writeAndFlush((String) result);
    }

    /**
     * 初始化springmvc组件
     *
     * @param config 配置类
     * @throws Exception
     */
    @Override
    public void init(Class<?> config) throws Exception {
        //初始化spring容器
        if (context == null) {
            context = new ApplicationContext(config);
            context.refresh();
        }
        //初始化mvc配置
        webConfig = new WebConfig("application.properties");
        //初始化springmvc的组件
        initStrategies(context);
    }

    public void initStrategies(ApplicationContext context) {
        //请求处理映射器(已实现)
        initHandlerMappings(context);

        //初始化参数处理器(已实现)
        initHandlerAdapters(context);

        //初始化视图解析器(已实现)
        initViewResolvers(context);

        //初始化视图预处理器
        initRequestToViewNameTranslator(context);
        //多文件上传的组件
        initMultipartResolver(context);
        //初始化本地语言环境
        initLocaleResolver(context);
        //初始化模板处理器
        initThemeResolver(context);
        //初始化异常拦截器
        initHandlerExceptionResolvers(context);
        //参数缓存器
        initFlashMapManager(context);
    }

    /**
     * 从spring上下文中解析所有的请求处理映射器
     *
     * @param context spring上下文
     */
    private void initHandlerMappings(ApplicationContext context) {
        //从content中解析处理器
        String[] beanNames = context.getBeanNames();
        for (String beanName : beanNames) {
            Object controller = context.getBean(beanName);
            Class<?> clazz = controller.getClass();
            //如果是cglib代理类，则需要使用其父类，即被代理类
            if (clazz.getName().contains("$$EnhancerByCGLIB$$")) {
                clazz = clazz.getSuperclass();
            }
            //找到有Controller的类
            if (clazz.isAnnotationPresent(Controller.class)) {
                //判断是否有公共路径
                String commonPath = "";
                if (clazz.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
                    commonPath = HandlerUtils.patternResolve(requestMapping.value());
                }
                //遍历类中的方法
                for (Method declaredMethod : clazz.getDeclaredMethods()) {
                    declaredMethod.setAccessible(true);
                    HandlerMapping handlerMapping = null;
                    //获取有Mapping的方法
                    if (declaredMethod.isAnnotationPresent(RequestMapping.class)) {
                        String path = HandlerUtils.patternResolve(declaredMethod.getAnnotation(RequestMapping.class).value());
                        handlerMapping = new HandlerMapping(Pattern.compile(commonPath + path), declaredMethod, controller);
                    } else if (declaredMethod.isAnnotationPresent(GetMapping.class)) {
                        String path = HandlerUtils.patternResolve(declaredMethod.getAnnotation(GetMapping.class).value());
                        handlerMapping = new HandlerMapping(Pattern.compile(commonPath + path), declaredMethod, controller);
                        handlerMapping.setType("GET");
                    } else if (declaredMethod.isAnnotationPresent(PostMapping.class)) {
                        String path = HandlerUtils.patternResolve(declaredMethod.getAnnotation(PostMapping.class).value());
                        handlerMapping = new HandlerMapping(Pattern.compile(commonPath + path), declaredMethod, controller);
                        handlerMapping.setType("POST");
                    }
                    //解析ResponseBody
                    if (handlerMapping != null) {
                        handlerMapping.setRender((!declaredMethod.isAnnotationPresent(ResponseBody.class)
                                && !clazz.isAnnotationPresent(ResponseBody.class)));
                        handlerMappings.add(handlerMapping);
                    }
                }
            }
        }
    }

    /**
     * 初始化映射处理器
     *
     * @param context
     */
    private void initHandlerAdapters(ApplicationContext context) {
        //给每一个HandlerMapping配置一个配置类
        for (HandlerMapping handlerMapping : handlerMappings) {
            handlerAdaptorMap.put(handlerMapping, new HandlerAdaptor());
        }
    }

    /**
     * 封装所有的视图文件解析器
     *
     * @param context
     */
    private void initViewResolvers(ApplicationContext context) {
        //初始化所有的ViewResolver，一个文件夹路径对应一个视图文件解析器
        String rootPath = this.getClass().getResource("/").getPath();
        String templateDir = rootPath + webConfig.getDefaultTemplateDir();
        File dir = new File(templateDir);
        //注册默认视图解析器
        viewResolvers.add(new ViewResolver(templateDir));
        //递归注册
        registViewResolvers(dir, templateDir);
    }

    private void registViewResolvers(File dir, String templateDir) {
        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                String dirName = templateDir + file.getName() + "/";
                ViewResolver viewResolver = new ViewResolver(dirName);
                viewResolvers.add(viewResolver);
                registViewResolvers(file, dirName);
            }
        }
    }

    private void initRequestToViewNameTranslator(ApplicationContext context) {

    }

    private void initHandlerExceptionResolvers(ApplicationContext context) {

    }

    private void initFlashMapManager(ApplicationContext context) {

    }

    private void initThemeResolver(ApplicationContext context) {

    }

    private void initLocaleResolver(ApplicationContext context) {

    }

    private void initMultipartResolver(ApplicationContext context) {

    }

    public ApplicationContext getContext() {
        return context;
    }

    public void setContext(ApplicationContext context) {
        this.context = context;
    }
}
